/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.datasource.routing;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.tianyun.cloud.datasource.annotation.RoutingWith;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 数据源处理拦截器
 *
 * @auther ebert_chan
 */
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RoutingWithAspect {

    @Around("@within(org.tianyun.cloud.datasource.annotation.RoutingWith) || @annotation(org.tianyun.cloud.datasource.annotation.RoutingWith)")
    public Object execute(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method implMethod = methodSignature.getMethod();

        // 优先查找方法上的注解
        RoutingWith routingWith = getMethodAnnotation(implMethod, RoutingWith.class);
        if (routingWith == null) {
            // 方法上没有再从类上找
            routingWith = this.getClassAnnotation(implMethod.getDeclaringClass(), RoutingWith.class);
        }
        // 如果没有注解，直接跳过
        if (routingWith == null) {
            return pjp.proceed();
        }

        DataSourceContext source = DataSourceContext.get();
        try {
            DataSourceContext.set(routingWith.value(), routingWith.slave());
            return pjp.proceed();
        } finally {
            if (source != null) {
                // 方法之前有设置过数据源需还原
                DataSourceContext.set(source);
            } else {
                // 未设置清除数据源
                DataSourceContext.clear();
            }

        }
    }

    private <T extends Annotation> T getMethodAnnotation(Method method, Class<T> annotationClass) {
        T annotation = method.getAnnotation(annotationClass);
        return annotation;
    }

    private <T extends Annotation> T getClassAnnotation(Class<?> target, Class<T> annotationClass) {
        T annotation = target.getAnnotation(annotationClass);
        return annotation;
    }

}
